﻿using System;
using System.ComponentModel;
using System.IO;
using FileSynchronizer.DataAccess;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Files;
using mylibrary;

namespace FileSynchronizer.Objects
{
    public class SyncProcessor
    {
        #region Private variables
        //---------------------------------------------------------------------------------------------------------------------------------------
        static BackgroundWorker myProcessor;

        private const int SourceChanged = 1;
        private const int DestinationChanged = 2;
        private bool IsBusy;
        private bool mySyncSuccessfull = false;

        private FolderPair myFolderPair;
        private FolderPairLog myFolderPairLog;

        private ProgressChangedEventHandler myExternalProgressHandler;
        private RunWorkerCompletedEventHandler myExternalCompletionHandler;
        //---------------------------------------------------------------------------------------------------------------------------------------
        #endregion
        
        #region Public variables and properties
        //---------------------------------------------------------------------------------------------------------------------------------------
        public SyncStatus DestinationSyncStatus;
        public SyncStatus SourceSyncStatus;

        public bool SyncSuccessfull
        {
            get
            {
                return mySyncSuccessfull;
            }
        }
        //---------------------------------------------------------------------------------------------------------------------------------------
        #endregion

        #region Constructors
        //---------------------------------------------------------------------------------------------------------------------------------------
 
        public SyncProcessor()
        {
            IsBusy = false;
            myExternalCompletionHandler = null;
            myExternalProgressHandler = null;

            myProcessor = new BackgroundWorker();
        }

        //---------------------------------------------------------------------------------------------------------------------------------------
        #endregion

        #region SyncFolderPair
        //---------------------------------------------------------------------------------------------------------------------------------------

        public void SyncFolderPair (FolderPair theFolderPair, ProgressChangedEventHandler ExternalProgressHandler, RunWorkerCompletedEventHandler ExternalCompletionHandler)
        {
            if (!IsBusy)
            {
                myFolderPair = theFolderPair;

                myExternalProgressHandler = ExternalProgressHandler;
                myExternalCompletionHandler = ExternalCompletionHandler;

                myProcessor.WorkerReportsProgress = true;
                myProcessor.WorkerSupportsCancellation = true;
                myProcessor.DoWork += new System.ComponentModel.DoWorkEventHandler(Processor_SyncFolderPairStart);
                myProcessor.ProgressChanged += Processor_SyncFolderPairProgressChanged;
                myProcessor.RunWorkerCompleted += Processor_SyncFolderPairCompleted;
                IsBusy = true;

                // Prepare Logging
                myFolderPairLog = new FolderPairLog();
                myFolderPairLog.FolderPair_ID = myFolderPair.ID;
                myFolderPairLog.RunDate = DateTime.Now;

                myProcessor.RunWorkerAsync(myFolderPair);
            }
        }

        private void Processor_SyncFolderPairStart(object sender, DoWorkEventArgs e)
        {
            FileSyncHelperClass myFileSyncHelperClass = new FileSyncHelperClass();
            FileSyncProvider sourceProvider = null;
            FileSyncProvider destinationProvider = null;

            FolderPair TheFolderPair = (FolderPair)e.Argument;

            try
            {
                //Initialize SyncStatus Structure
                SourceSyncStatus = new SyncStatus();
                DestinationSyncStatus = new SyncStatus();

                mySyncSuccessfull = false;

                if (Directory.Exists(TheFolderPair.LeftFolder) & Directory.Exists(TheFolderPair.RightFolder))
                {
                    // Update latency
                    myFolderPairLog.LeftLatency = NetworkDriveHelper.PathLatency(TheFolderPair.LeftFolder);
                    myFolderPairLog.RightLatency = NetworkDriveHelper.PathLatency(TheFolderPair.RightFolder);

                    // Create file system provider
                    sourceProvider = myFileSyncHelperClass.CreateFileSyncprovider(TheFolderPair.LeftIdentifier, TheFolderPair.LeftFolder);
                    destinationProvider = myFileSyncHelperClass.CreateFileSyncprovider(TheFolderPair.RightIdentifier, TheFolderPair.RightFolder);

                    // Set up Event Triggering
                    sourceProvider.AppliedChange += new EventHandler<AppliedChangeEventArgs>(OnSyncFolderPairAppliedSourceChange);
                    destinationProvider.AppliedChange += new EventHandler<AppliedChangeEventArgs>(OnSyncFolderPairAppliedDestinationChange);

                    if ((sourceProvider != null) & (destinationProvider != null))
                    {
                        // Ask providers to detect changes
                        sourceProvider.DetectChanges();
                        destinationProvider.DetectChanges();

                        SyncOrchestrator agent = new SyncOrchestrator();
                        agent.LocalProvider = sourceProvider;
                        agent.RemoteProvider = destinationProvider;
                        agent.Direction = SyncDirectionOrder.UploadAndDownload;
                        agent.Synchronize();
                        mySyncSuccessfull = true;
                    }
                }
                else
                {
                    if (!Directory.Exists(TheFolderPair.LeftFolder))
                    {
                        SourceSyncStatus.ErrorStatus = SyncStatusErrors.PathNotAccessible;
                    }
                    if (!Directory.Exists(TheFolderPair.RightFolder))
                    {
                        DestinationSyncStatus.ErrorStatus = SyncStatusErrors.PathNotAccessible;
                    }
                    mySyncSuccessfull = false;
                }
            }
            finally
            {
                // Release resources
                if (sourceProvider != null) sourceProvider.Dispose();
                if (destinationProvider != null) destinationProvider.Dispose();
            }
        }

        private void OnSyncFolderPairAppliedSourceChange(object sender, AppliedChangeEventArgs args)
        {
            switch (args.ChangeType)
            {
                case ChangeType.Create:
                    SourceSyncStatus.AddFileCreated();
                    break;
                case ChangeType.Delete:
                    SourceSyncStatus.AddFileDeleted();
                    break;
                case ChangeType.Rename:
                    SourceSyncStatus.AddFileRenamed();
                    break;
                case ChangeType.Update:
                    SourceSyncStatus.AddFileUpdated();
                    break;
            }
            myProcessor.ReportProgress(SourceChanged);
        }

        private void OnSyncFolderPairAppliedDestinationChange(object sender, AppliedChangeEventArgs args)
        {
            switch (args.ChangeType)
            {
                case ChangeType.Create:
                    DestinationSyncStatus.AddFileCreated();
                    break;
                case ChangeType.Delete:
                    DestinationSyncStatus.AddFileDeleted();
                    break;
                case ChangeType.Rename:
                    DestinationSyncStatus.AddFileRenamed();
                    break;
                case ChangeType.Update:
                    DestinationSyncStatus.AddFileUpdated();
                    break;
            }
            myProcessor.ReportProgress(DestinationChanged);
        }

        private void Processor_SyncFolderPairProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (myExternalProgressHandler != null)
            {
                myExternalProgressHandler(sender, e);
            }
        }
        
        private void Processor_SyncFolderPairCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            SyncDatabaseHelper mySyncDatabaseHelper = new SyncDatabaseHelper();

            myFolderPairLog.RunDuration = (DateTime.Now - myFolderPairLog.RunDate).TotalSeconds;
            myFolderPairLog.RunSuccessfull = mySyncSuccessfull;

            if (SourceSyncStatus != null)
            {
                if (SourceSyncStatus.ErrorStatus == SyncStatusErrors.PathNotAccessible)
                {
                    //Program.TheErrorForm.AddErrorString("Unable to access " + myFolderPair.LeftFolder);
                    myFolderPairLog.ErrorMessage = "Unable to access " + myFolderPair.LeftFolder;
                }
                else
                {
                    myFolderPairLog.LeftCreated = SourceSyncStatus.FilesCreatedCount;
                    myFolderPairLog.LeftDeleted = SourceSyncStatus.FilesDeletedCount;
                    myFolderPairLog.LeftRenamed = SourceSyncStatus.FilesRenamedCount;
                    myFolderPairLog.LeftUpdated = SourceSyncStatus.FilesUpdatedCount;
                }
            }

            if (DestinationSyncStatus != null)
            {
                if (DestinationSyncStatus.ErrorStatus == SyncStatusErrors.PathNotAccessible)
                {
                    //Program.TheErrorForm.AddErrorString("Unable to access " + myFolderPair.RightFolder);
                    myFolderPairLog.ErrorMessage = "Unable to access " + myFolderPair.RightFolder;
                }
                else
                {
                    myFolderPairLog.RightCreated = DestinationSyncStatus.FilesCreatedCount;
                    myFolderPairLog.RightDeleted = DestinationSyncStatus.FilesDeletedCount;
                    myFolderPairLog.RightRenamed = DestinationSyncStatus.FilesRenamedCount;
                    myFolderPairLog.RightUpdated = DestinationSyncStatus.FilesUpdatedCount;
                }
            }

            mySyncDatabaseHelper.AddFolderPairLog(myFolderPairLog);

            myProcessor.DoWork -= Processor_SyncFolderPairStart;
            myProcessor.ProgressChanged -= Processor_SyncFolderPairProgressChanged;

            myProcessor.RunWorkerCompleted -= Processor_SyncFolderPairCompleted;
            myProcessor.WorkerReportsProgress = false;


            IsBusy = false;
            if (myExternalCompletionHandler != null)
            {
                myExternalCompletionHandler(sender, e);
            }
        }
        //---------------------------------------------------------------------------------------------------------------------------------------
        #endregion

   }
}
